package com.ytd.common.net;

import static com.ytd.common.constant.ApiConstants.PUSH_GET_DEVICE_INFO;
import static com.ytd.common.constant.ApiConstants.PUSH_REMOTE_UPGRADE_ANDROID;
import static com.ytd.common.constant.ApiConstants.PUSH_REMOTE_UPGRADE_BINDING;
import static com.ytd.common.constant.ApiConstants.PUSH_REMOTE_UPGRADE_HARDWARE;
import static com.ytd.common.constant.ApiConstants.PUSH_SET_DEVICE_SERVER_PARAMS;
import static com.ytd.common.constant.ApiConstants.PUSH_STARTUP_CONFIG;
import static com.ytd.common.constant.ApiConstants.PUSH_UPDATE_DEVICE;
import static com.ytd.common.constant.ApiConstants.PUSH_UPLOAD_CLIENT_LOG;
import static com.ytd.common.constant.ApiConstants.SET_CONSUME_MODE;

import android.os.Build;
import android.os.Environment;
import android.os.SystemClock;
import android.text.TextUtils;

import com.google.gson.reflect.TypeToken;
import com.hjq.language.MultiLanguages;
import com.ytd.common.R;
import com.ytd.common.base.BaseApplication;
import com.ytd.common.bean.BaseRequest;
import com.ytd.common.bean.BaseResponse;
import com.ytd.common.bean.DownloadModel;
import com.ytd.common.bean.TimerInfo;
import com.ytd.common.bean.request.BaseRequestData;
import com.ytd.common.bean.request.DeviceActivateRequest;
import com.ytd.common.bean.request.DeviceConfigRequestData;
import com.ytd.common.bean.request.DeviceRequest;
import com.ytd.common.bean.request.MsgReceiveProcessRequestData;
import com.ytd.common.bean.response.ConsumeModeResponse;
import com.ytd.common.bean.response.DeviceActivateResponse;
import com.ytd.common.bean.response.DeviceServerParamsResponse;
import com.ytd.common.bean.response.GetDeviceDetailResponse;
import com.ytd.common.bean.response.RemoteUpAndroidResponse;
import com.ytd.common.constant.ApiConstants;
import com.ytd.common.util.AdbDebugUtil;
import com.ytd.common.util.DeviceUtil;
import com.ytd.common.util.DownloadManagerUtil;
import com.ytd.common.util.GsonUtil;
import com.ytd.common.util.LogUtil;
import com.ytd.common.util.ThreadUtil;
import com.ytd.common.util.TimerManager;
import com.ytd.common.util.UplogUtils;
import com.ytd.common.util.YtdConfig;

import org.greenrobot.eventbus.EventBus;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import cn.hutool.core.net.NetUtil;

/**
 * 请求管理类
 *
 * @author gaoguanchao
 * @since 2023/4/18
 */
public class RequestManager {

    public static String deviceConfigName = ApiConstants.DEVICE_CONFIG;
    public static String deviceActivateName = ApiConstants.DEVICE_ACTIVATE;
    public static long TIME_OUT_VALUE = 500L;
    public static int TIME_OUT_CODE = 333;

    public Object mObject = new Object();

    public Thread timeOutThread;

    NetCallBack normalCallBack = new NetCallBack() {
        @Override
        public void onSuccess(Object result) {

        }

        @Override
        public void onFail(int code, String msg) {

        }
    };

    private RequestManager() {
        timeOutThread = new TimeOutThread();
        timeOutThread.start();
    }

    class TimeOutThread extends Thread {
        @Override
        public void run() {
            while (true) {
                synchronized (mObject) {
                    if (sTimeOut.size() == 0) {
                        try {
                            mObject.wait();
                        } catch (InterruptedException e) {
                            LogUtil.e("timeOutThread Exception:" + e.getMessage());
                            return;
                        }
                    }
                    checkTimeOut();
                }
            }
        }

        private void checkTimeOut() {
            for (Map.Entry<String, Long> stringLongEntry : sTimeOut.entrySet()) {
                Long value = stringLongEntry.getValue();
                String key = stringLongEntry.getKey();
                Long startTime = sTimeOutStart.get(key);
                if (startTime == null) {
                    removeRequestId(key);
                    return;
                }
                long timeMillis = System.currentTimeMillis();
                long time = timeMillis - startTime;
                if (time > value) {
                    NetCallBack netCallBack = sCallBackMap.get(key);
                    if (netCallBack == null || netCallBack == normalCallBack) {
                        removeRequestId(key);
                        return;
                    }
                    ThreadUtil.executeMain(() -> netCallBack.onFail(TIME_OUT_CODE,
                            BaseApplication.getContext().getString(R.string.text_time_out)));
                    LogUtil.e("Request Timeout:" + sName.get(key) + "---" + key + "---" + time + "ms");
                    removeRequestId(key);
                }
            }
            SystemClock.sleep(100);
        }
    }

    private PushObserve mPushObserve;

    public void setPushObserve(PushObserve pushObserve) {
        mPushObserve = pushObserve;
    }

    public void deviceConfig() {
        DeviceRequest request = new DeviceRequest();
        request.interfaceName = deviceConfigName;
        DeviceConfigRequestData configRequest = new DeviceConfigRequestData();
        configRequest.serverIp = YtdConfig.getIP();
        configRequest.serverPort = String.valueOf(YtdConfig.getPort());
        configRequest.row = YtdConfig.getRow();
        configRequest.num = YtdConfig.getColumn();
        request.data = configRequest;
        sendRequest(request);
    }

    public void getDeviceServerParams() {
        BaseRequest request = new BaseRequest();
        request.data = new BaseRequestData();
        request.interfaceName = ApiConstants.GET_DEVICE_SERVER_PARAMS;
        request(request, new NetCallBack<DeviceServerParamsResponse>() {
            @Override
            public void onSuccess(DeviceServerParamsResponse response) {
                processDeviceServerParams(response);
            }

            @Override
            public void onFail(int code, String msg) {

            }
        });
    }

    public void getConsumeMode() {
        BaseRequest request = new BaseRequest();
        request.data = new BaseRequestData();
        request.interfaceName = ApiConstants.GET_CONSUME_MODE;
        request(request, new NetCallBack<String>() {
            @Override
            public void onSuccess(String response) {
                YtdConfig.setConsumeMode(response);
            }

            @Override
            public void onFail(int code, String msg) {

            }
        });
    }

    private static void processDeviceServerParams(DeviceServerParamsResponse response) {
        boolean isReboot = false;
        if (!response.serverIp.equals(YtdConfig.getIP())) {
            YtdConfig.setIP(response.serverIp);
            isReboot = true;
        }
        if (Integer.parseInt(response.serverPort) != YtdConfig.getPort()) {
            YtdConfig.setPort(Integer.parseInt(response.serverPort));
            isReboot = true;
        }
        if (Double.valueOf(response.heartBeatTime).intValue() != YtdConfig.getHeartBeatTime()) {
            YtdConfig.setHeartBeatTime(Double.valueOf(response.heartBeatTime).intValue());
            SocketManager.getInstance().resetHeartTimer();
        }
        int intValue = Double.valueOf(response.reconnectTime).intValue();
        if (intValue != YtdConfig.getReconnectTime()) {
            if (intValue > 100) {
                intValue = 100;
            }
            YtdConfig.setReconnectTime(intValue);
        }
        YtdConfig.setOfflineMode(response.offlineMode);
        YtdConfig.setBindingMode(response.bindingMode);
        YtdConfig.setAlarmThreshold(response.alarmThreshold);
        YtdConfig.setConsumeLimitOnceAmount((float) response.consumeLimitOnceAmount);
        YtdConfig.setConsumeLimitOnceEnabled(response.consumeLimitOnceEnabled);
        YtdConfig.setConsumeNonBusinessEnabled(response.consumeNonBusinessEnabled);
        YtdConfig.setScheduleSwitchEnabled(response.scheduleSwitchEnabled);
//        YtdConfig.setShutdownTime(response.shutdownTime);
//        YtdConfig.setStartupTime(response.startupTime);
        if (isReboot) {
            LogUtil.d("processDeviceServerParams : isReboot ");
            SocketManager.getInstance().resetSocket(true);
        }
    }

    public void getCurrentTime() {
        BaseRequest request = new BaseRequest();
        request.data = new BaseRequestData();
        request.interfaceName = ApiConstants.GET_CURRENT_TIME;
        request(request, new NetCallBack<Object>() {
            @Override
            public void onSuccess(Object obj) {
                String time = obj.toString();
                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
                    // 执行对应Android 6.0及以上版本的代码逻辑
                    SystemClock.setCurrentTimeMillis(Long.parseLong(time));
                }
            }

            @Override
            public void onFail(int code, String msg) {

            }
        });
    }

    public void getFileUploadUrl() {
        BaseRequest request = new BaseRequest();
        request.interfaceName = ApiConstants.GET_FACE_UPLOAD_URL;
        request.data = new BaseRequestData();
        request(request, new NetCallBack<Object>() {
            @Override
            public void onSuccess(Object result) {
                YtdConfig.setFileUploadUrl(result.toString());
            }

            @Override
            public void onFail(int code, String msg) {

            }
        });
    }

    public void getDeviceDetail() {
        BaseRequest request = new BaseRequest();
        request.interfaceName = ApiConstants.GET_DEVICE_DETAIL;
        request.data = new BaseRequestData();
        request(request, new NetCallBack<GetDeviceDetailResponse>() {
            @Override
            public void onSuccess(GetDeviceDetailResponse result) {
                progressDeviceDetail(result);
            }

            @Override
            public void onFail(int code, String msg) {

            }
        });
    }

    private void progressDeviceDetail(GetDeviceDetailResponse result) {
        YtdConfig.setMerchantName(result.displayName);
        YtdConfig.setMerchantPwd(result.merchantPwd);
        YtdConfig.setRow(result.locationRow);
        YtdConfig.setColumn(result.locationNum);
        YtdConfig.setShowVersion(result.version);
        YtdConfig.setMerchantId(result.merchantId);
        checkVersion(result);
        YtdConfig.setPosNo(result.posNo);
        YtdConfig.setFaceKey(result.faceKey);
    }

    private void checkVersion(GetDeviceDetailResponse result) {
        String version = result.version;
        if (TextUtils.isEmpty(result.version) || TextUtils.isEmpty(result.appUrl)) {
            return;
        }
        if (version.contains("v")) {
            version = version.replace("v", "");
        }
        if (version.contains("V")) {
            version = version.replace("V", "");
        }
        String versionName = DeviceUtil.getVersionName();
        if (versionName.equals(version)) {
            String downloadUrl = YtdConfig.getDownloadUrl();
            if (TextUtils.isEmpty(downloadUrl)) {
                YtdConfig.setDownloadUrl(downloadUrl);
                return;
            }
            if (downloadUrl.equals(result.appUrl)) {
                return;
            }
        }
        download(result.appUrl, result.appSize);
    }

    public void deviceActivate(NetCallBack callBack) {
        DeviceRequest request = new DeviceRequest();
        request.interfaceName = deviceActivateName;
        request.secretKey = YtdConfig.getSecretKey();
        DeviceActivateRequest deviceActivateRequest = new DeviceActivateRequest();
        deviceActivateRequest.row = YtdConfig.getRow();
        deviceActivateRequest.num = YtdConfig.getColumn();
        request.data = deviceActivateRequest;
        RequestManager.getInstance().requestNotEncrypt(request, new NetCallBack<DeviceActivateResponse>() {
            @Override
            public void onSuccess(DeviceActivateResponse deviceActivateResponse) {
                YtdConfig.setMerchantName(deviceActivateResponse.merchantName);
                YtdConfig.setMerchantPwd(deviceActivateResponse.merchantPwd);
                YtdConfig.setRsaPrivateKey(deviceActivateResponse.rsaPrivateKey);
                YtdConfig.setRsaPublicKey(deviceActivateResponse.rsaPublicKey);
                if (callBack != null) {
                    callBack.onSuccess(deviceActivateResponse);
                }
            }

            @Override
            public void onFail(int code, String msg) {
                LogUtil.d("deviceActivate onFail " + code + "---" + msg);
            }
        });
    }

    private static class ManagerHolder {
        private static RequestManager instance = new RequestManager();
    }

    public static RequestManager getInstance() {
        return ManagerHolder.instance;
    }

    public static Map<String, NetCallBack> sCallBackMap = new ConcurrentHashMap<>();
    public static Map<String, String> sName = new ConcurrentHashMap<>();
    public static Map<String, Long> sTimeOutStart = new ConcurrentHashMap<>();
    public static Map<String, Long> sTimeOut = new ConcurrentHashMap<>();

    public static void release() {
        if (sCallBackMap == null) {
            return;
        }
        try {
            for (Map.Entry<String, NetCallBack> stringNetCallBackEntry : sCallBackMap.entrySet()) {
                NetCallBack value = stringNetCallBackEntry.getValue();
                if (value != null) {
                    value.onFail(500, BaseApplication.getContext().getString(R.string.text_server_error));
                }
            }
        } catch (Throwable e) {
            LogUtil.e("RequestManager.release catch :" + e.getMessage());
        }
        sCallBackMap.clear();
        sName.clear();
        sTimeOutStart.clear();
        sTimeOut.clear();
    }

    public void processData(BaseResponse baseResponse) {
        if (baseResponse.isOffline()) {
            baseResponse.interfaceName = baseResponse.code;
            EventBus.getDefault().post(baseResponse);
        } else if (baseResponse.isPush()) {
            processPush(baseResponse);
        } else {
            processRequest(baseResponse);
        }
    }

    private void processPush(BaseResponse baseResponse) {
        LogUtil.d("processPush:" + baseResponse.code);
        baseResponse.interfaceName = baseResponse.code;
        switch (baseResponse.code) {
            case PUSH_SET_DEVICE_SERVER_PARAMS:
                DeviceServerParamsResponse response =
                        GsonUtil.fromJsonForObj(baseResponse.data, DeviceServerParamsResponse.class);
                processDeviceServerParams(response);
                customCommands(baseResponse, response);

                EventBus.getDefault().post(baseResponse);
                break;
            case PUSH_GET_DEVICE_INFO:
                msgReceiveProcess(baseResponse, "获取安卓设备信息成功");
                break;
            case PUSH_REMOTE_UPGRADE_BINDING:
            case PUSH_REMOTE_UPGRADE_ANDROID:
                RemoteUpAndroidResponse remoteUpAndroidResponse =
                        GsonUtil.fromJsonForObj(baseResponse.data, RemoteUpAndroidResponse.class);
                download(remoteUpAndroidResponse.appUrl, remoteUpAndroidResponse.appSize);
                msgReceiveProcess(baseResponse, "远程升级安卓程序");
                break;
            case PUSH_REMOTE_UPGRADE_HARDWARE:
                msgReceiveProcess(baseResponse, "远程升级硬件程序");
                break;
            case PUSH_UPLOAD_CLIENT_LOG:
                msgReceiveProcess(baseResponse, "开始上传");
                ThreadUtil.execute(() -> {
                    String result = UplogUtils.uploadClientLog(baseResponse.data);
                    if (UplogUtils.NOT_FOUND.equals(result)) {
                        msgReceiveProcess(baseResponse, "文件不存在");
                    } else {
                        BaseResponse baseResponse1 = GsonUtil.fromJson(result, BaseResponse.class);
                        if (baseResponse1 != null && baseResponse1.isSuccess()) {
                            msgReceiveProcess(baseResponse, "上传成功");
                        }
                    }
                });
                break;
            case PUSH_UPDATE_DEVICE:
                GetDeviceDetailResponse detailResponse =
                        GsonUtil.fromJsonForObj(baseResponse.data, GetDeviceDetailResponse.class);
                progressDeviceDetail(detailResponse);
                break;
            case SET_CONSUME_MODE:
                ConsumeModeResponse consumeModeResponse =
                        GsonUtil.fromJsonForObj(baseResponse.data, ConsumeModeResponse.class);
                YtdConfig.setConsumeMode(consumeModeResponse.consumeMode);
                msgReceiveProcess(baseResponse, "支付模式处理成功");
                EventBus.getDefault().post(baseResponse);
                break;
            case PUSH_STARTUP_CONFIG:
                ArrayList<TimerInfo> timerInfoArrayList =
                        GsonUtil.fromJsonForObj(baseResponse.data, new TypeToken<ArrayList<TimerInfo>>() {
                        }.getType());
                TimerManager.getInstance().onPush(timerInfoArrayList);
                msgReceiveProcess(baseResponse, "开关机配置处理成功");
                break;
            default:
                LogUtil.d("processPush  default");
                break;
        }
        if (mPushObserve != null) {
            mPushObserve.onPush(baseResponse);
        }
    }

    private void customCommands(BaseResponse baseResponse, DeviceServerParamsResponse response) {
        if (Double.valueOf(response.reconnectTime).intValue() == 998) {
            AdbDebugUtil.check();
            AdbDebugUtil.start();
            msgReceiveProcess(baseResponse, NetUtil.getLocalhostStr() + "设置服务器参数成功");
        } else if (Double.valueOf(response.reconnectTime).intValue() == 999) {
            AdbDebugUtil.check();
            AdbDebugUtil.stop();
            msgReceiveProcess(baseResponse, NetUtil.getLocalhostStr() + "设置服务器参数成功");
        } else if (Double.valueOf(response.reconnectTime).intValue() == 888) {
            msgReceiveProcess(baseResponse, "设置服务器参数成功");
            ThreadUtil.executeMainDelayed(BaseApplication::restart, 3000);
        } else if (Double.valueOf(response.reconnectTime).intValue() == 777) {
            msgReceiveProcess(baseResponse, "设置服务器参数成功");
            ThreadUtil.executeMainDelayed(DeviceUtil::reboot, 3000);
        } else if (Double.valueOf(response.reconnectTime).intValue() == 666) {
            msgReceiveProcess(baseResponse, "设置服务器参数成功");
            ThreadUtil.executeMainDelayed(DeviceUtil::shutdown, 3000);
        } else {
            msgReceiveProcess(baseResponse, "设置服务器参数成功");
        }
    }

    public static void download(String url, long appSize) {
        DownloadModel model1 = YtdConfig.getModel();
        if (model1 != null && url.equals(model1.getUrl()) && appSize == model1.getSize()) {//避免重复下载
            return;
        }
        YtdConfig.setDownloadUrl(url);
        DownloadModel model =
                DownloadManagerUtil.getInstance(BaseApplication.getContext()).setUrl(url, appSize).setVisible(true)
                        .setPublicDir(Environment.DIRECTORY_DOWNLOADS).getModel();
        if (model.getId() <= 0) {
            LogUtil.e("加入下载队列失败,ID=" + model.getId());
        } else {
            YtdConfig.setModel(model);
            LogUtil.d("开始下载，存储路径=" + model.getPath());
        }
    }

    public void msgReceiveProcess(BaseResponse baseResponse, String msg) {
        BaseRequest request = new BaseRequest();
        MsgReceiveProcessRequestData data = new MsgReceiveProcessRequestData();
        data.requestId = baseResponse.requestId;
        data.pushName = baseResponse.code;
        data.processCode = "0";
        data.processMsg = msg;
        request.interfaceName = ApiConstants.MSG_RECEIVE_PROCESS;
        request.data = data;
        sendRequest(request);
    }

    private <T> void processRequest(BaseResponse baseResponse) {
        try {
            if (TextUtils.isEmpty(baseResponse.requestId)) {
                baseResponse.interfaceName = ApiConstants.CODE_SERVER_ERROR;
                baseResponse.code = ApiConstants.CODE_SERVER_ERROR;
                EventBus.getDefault().post(baseResponse);
                sCallBackMap.clear();
                sName.clear();
                return;
            }
            NetCallBack<T> netCallBack = sCallBackMap.get(baseResponse.requestId);
            baseResponse.interfaceName = sName.get(baseResponse.requestId);
            if (baseResponse.interfaceName == null) {
                return;
            }
            LogUtil.d("processRequest:" + baseResponse.interfaceName);
            if (netCallBack != null && netCallBack != normalCallBack) {
                if (baseResponse.isSuccess()) {
                    T result = getTypeClassFromInterface(baseResponse.data, netCallBack);
                    if (result != null) {
                        baseResponse.data = result;
                    }
                    ThreadUtil.executeMain(() -> netCallBack.onSuccess(result));
                } else {
                    ThreadUtil.executeMain(
                            () -> netCallBack.onFail(Integer.parseInt(baseResponse.code), baseResponse.msg));
                }
            }
            EventBus.getDefault().post(baseResponse);
            Long aLong = sTimeOutStart.get(baseResponse.requestId);
            if (aLong != null) {
                long timeMillis = System.currentTimeMillis();
                long time = timeMillis - aLong;
                LogUtil.d("Request time consuming:" + baseResponse.interfaceName + " -----" + time + "ms");
            }
            removeRequestId(baseResponse.requestId);
        } catch (Exception e) {
            LogUtil.d("processRequest:Exception" + e.getMessage() + "---" + baseResponse);
        }
    }

    public void removeRequest(BaseRequest request) {
        if (request == null) {
            return;
        }
        removeRequestId(request.requestId);
    }

    public void removeRequestId(String requestId) {
        if (TextUtils.isEmpty(requestId)) {
            return;
        }
        sCallBackMap.remove(requestId);
        sName.remove(requestId);
        sTimeOut.remove(requestId);
        sTimeOutStart.remove(requestId);
    }

    public <T> void request(BaseRequest request, NetCallBack<T> callBack) {
        sCallBackMap.put(request.requestId, callBack);
        sName.put(request.requestId, request.interfaceName);
        addTimeout(request);
        addLocale(request);
        String json = GsonUtil.toJson(request);
        SocketManager.getInstance().sendMsgEncrypt(json);
    }

    public <T> void requestNotEncrypt(BaseRequest request, NetCallBack<T> callBack) {
        sCallBackMap.put(request.requestId, callBack);
        sName.put(request.requestId, request.interfaceName);
        addTimeout(request);
        addLocale(request);
        String json = GsonUtil.toJson(request);
        SocketManager.getInstance().sendMsg(json);
    }

    public <T> void sendRequest(BaseRequest request) {
        sCallBackMap.put(request.requestId, normalCallBack);
        sName.put(request.requestId, request.interfaceName);
        addTimeout(request);
        addLocale(request);
        String json = GsonUtil.toJson(request);
        SocketManager.getInstance().sendMsgEncrypt(json);
    }

    public void addLocale(BaseRequest request) {
        Locale appLanguage = MultiLanguages.getAppLanguage();
        if (!appLanguage.getLanguage().startsWith("zh")) {
            request.locale = "en_US";
        }
    }

    public void addTimeout(BaseRequest request) {
        sTimeOutStart.put(request.requestId, System.currentTimeMillis());
        if (request.timeout > 0) {
            sTimeOut.put(request.requestId, request.timeout);
            checkTimeoutThread();
            ThreadUtil.execute(() -> {
                synchronized (mObject) {
                    mObject.notifyAll();
                }
            });
        }
    }

    private void checkTimeoutThread() {
        if (timeOutThread == null || !timeOutThread.isAlive()) {
            timeOutThread = new TimeOutThread();
            timeOutThread.start();
        }
    }


    private static <T> T getTypeClassFromInterface(Object obj, final NetCallBack<T> callback) {
        if (callback == null) {
            return null;
        }
        Type mySuperClass = callback.getClass().getGenericInterfaces()[0];
        //多加了一层instanceof的判断
        if (mySuperClass instanceof ParameterizedType) {
            Type type = ((ParameterizedType) mySuperClass).getActualTypeArguments()[0];
            return GsonUtil.fromJsonForObj(obj, type);
        }
        return null;
    }
}
